package org.sgmiller.quickstem.collections;

import org.sgmiller.quickstem.Stem;
import org.sgmiller.quickstem.Transaction;

public class TransactionalMultiArray<E> {

	int[] dimensions;
	Stem<E>[] values;
	E defaultValue;
	
	public TransactionalMultiArray(int[] dimensions) {
		this(null, dimensions);
	}
	
	@SuppressWarnings("unchecked")
	public TransactionalMultiArray(E defaultValue, int[] dimensions) {
		this.dimensions=dimensions;
		int size=dimensions[0];
		for (int i=1; i<dimensions.length; i++) {
			size*=dimensions[i];
		}
		values=new Stem[size];
		this.defaultValue=defaultValue;
	}
	
	public E get(int... indices) {
		Stem<E> box=getBox(indices);
		return box.get();
	}

	public E get(int i1) {
		Stem<E> box=getBox(i1);
		return box.get();		
	}
	
	public E get(int i1, int i2) {
		Stem<E> box=getBox(i1, i2);
		return box.get();		
	}
	
	public E set(E value, int i1) {
		Stem<E> box=getBox(i1);
		return setBox(box, value);
	}

	private E setBox(Stem<E> box, E value) {
		Transaction current=Transaction.current();
		E oldValue=current.get(box);
		box.set(current, value);
		return oldValue;
	}

	public E set(E value, int i1, int i2) {
		Stem<E> box=getBox(i1, i2);
		return setBox(box, value);
	}

	public E set(E value, int... indices) {
		Stem<E> box=getBox(indices);
		return setBox(box, value);
	}

	private Stem<E> getBox(int i1, int i2) {
		return getBox(dimensions[0]*i1 + i2);
	}
	
	private Stem<E> getBox(int[] indices) {
		int idx=0;
		for (int i=0; i<dimensions.length-1; i++) {
			idx+=dimensions[i]*indices[i];
		}
		idx+=indices[dimensions.length-1];

		return getBox(idx);
	}


	private Stem<E> getBox(int idx) {
		if (values[idx]==null) {
			Stem<E> v=new Stem<E>(defaultValue);
			synchronized (this) { 
				if (values[idx] == null) {
					values[idx] = v;
				}
			}
		}
		return values[idx];
	}
}
